package com.lipengcheng.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.lipengcheng.feign.clients.ItemFeignClient;
import com.lipengcheng.feign.common.dto.Item;
import com.lipengcheng.feign.common.dto.PageDTO;
import com.lipengcheng.search.pojo.ItemDOC;
import com.lipengcheng.search.pojo.RequestParams;
import com.lipengcheng.search.service.SearchService;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private RestHighLevelClient client;

    /**
     * 根据搜索条件搜索文档
     *
     * @param param
     * @return
     */
    @Override
    public PageDTO<ItemDOC> page(RequestParams param) {
        try {
            //1.准备request对象
            SearchRequest request = new SearchRequest("item");
            //2.准备DSL
            //2.1准备query
            //构建BoolQuery
            buildBasicQuery(param, request);
            //2.2.分页
            int page = param.getPage();
            int size = param.getSize();
            request.source().from((page - 1) * size).size(size);
            //高亮
            request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
            //排序
            String sort = param.getSortBy();
            if ("sold".equals(sort)) {
                request.source().sort(sort, SortOrder.DESC);
            } else if ("price".equals(sort)) {
                request.source().sort(sort, SortOrder.ASC);
            }
            //3.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //4.解析响应
            return handleResponse(response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //根据id新增
    @Autowired
    ItemFeignClient feignClient;

    @Override
    public void saveItemById(Long id) {
        try {
            //查询数据
            Item item = feignClient.getById(id);
            //创建request对象
            IndexRequest request = new IndexRequest("item").id(id.toString());
            //将item转json
            String json = JSON.toJSONString(item);
            request.source(json, XContentType.JSON);
            //发送请求
            client.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //根据id删除
    @Override
    public void deleteItemById(Long id) {
        try {
            //1.创建request对象
            DeleteRequest request = new DeleteRequest("item", id.toString());
            //2.发送删除请求
            client.delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void buildBasicQuery(RequestParams param, SearchRequest request) {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        String key = param.getKey();
        //如果key为空直接查询所有
        if (key == null || key.equals("")) {
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            boolQuery.must(QueryBuilders.matchQuery("all", key));
        }
        //分类条件
        if (param.getCategory() != null && !param.getCategory().equals("")) {
            boolQuery.filter(QueryBuilders.termQuery("category", param.getCategory()));
        }
        //品牌条件
        if (param.getBrand() != null && !param.getBrand().equals("")) {
            boolQuery.filter(QueryBuilders.termQuery("brand", param.getBrand()));
        }
        //价格条件
        if (param.getMaxPrice() != null && param.getMinPrice() != null) {
            boolQuery.filter(QueryBuilders
                    .rangeQuery("price")
                    .gte(param.getMinPrice()*100)
                    .lte(param.getMaxPrice()*100));
        }
        FunctionScoreQueryBuilder queryBuilder = QueryBuilders.functionScoreQuery(
                boolQuery,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(100)
                        )
                }
        );
        request.source().query(queryBuilder);
    }

    private PageDTO<ItemDOC> handleResponse(SearchResponse response) {
        //4.获取hits数据
        SearchHits searchHits = response.getHits();
        //4.1获取总条目数
        long total = searchHits.getTotalHits().value;
        //4.2获取文档的数组
        SearchHit[] hits = searchHits.getHits();
        //4.3遍历数组
        List<ItemDOC> itemDOCList = new ArrayList<>(); //新建一个集合接受数据
        for (SearchHit hit : hits) {
            //获取文档source
            String json = hit.getSourceAsString();
            //返序列化
            ItemDOC itemDOC = JSON.parseObject(json, ItemDOC.class);
            //获取高亮结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                //根据高亮的字段名回去结果
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField != null) {
                    String name = highlightField.getFragments()[0].string();
                    itemDOC.setName(name);
                }
            }
            //存入集合
            itemDOCList.add(itemDOC);
        }
        //封装返回
        return new PageDTO<ItemDOC>(total, itemDOCList);
    }
}
